package qiyu.live.living.provider.config;

import jakarta.annotation.Resource;
import org.idea.qiyu.live.framework.redis.starter.key.LivingRoomProviderCacheKeyBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import qiyu.live.living.dto.resp.LivingRoomRespDTO;
import qiyu.live.living.enums.LivingRoomTypeEnum;
import qiyu.live.living.provider.service.ILivingRoomService;

import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * Description: Spring服务一启动的时候我们就开启一个定时任务,查询数据库刷新到redis中
 * User: sfx
 * Date: 2023-10-25
 * Time: 20:14
 */
@Component
public class RefreshLivingRoomListJob implements InitializingBean {

    private static Logger logger = LoggerFactory.getLogger(RefreshLivingRoomListJob.class);

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private LivingRoomProviderCacheKeyBuilder cacheKeyBuilder;

    @Resource
    ILivingRoomService livingRoomService;

    // 定时任务的线程池
    private ScheduledThreadPoolExecutor poolExecutor = new ScheduledThreadPoolExecutor(1);

    @Override
    public void afterPropertiesSet() throws Exception {
        poolExecutor.scheduleWithFixedDelay(new RefreshCacheListJob(), 3000, 1000, TimeUnit.MILLISECONDS);
    }

    class RefreshCacheListJob implements Runnable {
        @Override
        public void run() {
            // 因为我们是以分布式集群部署的,所以可能就会有多个JVM进程来执行这个任务,我们要保证只有一个进程执行这个任务
            // 不能多个进程都执行,会有安全问题,我们这里使用分布式锁
            String lockKey = cacheKeyBuilder.buildLivingRoomLock();
            boolean isSuccess = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 1, TimeUnit.SECONDS));
            if (isSuccess) {
                logger.info("开始从DB刷新到Redis");
                refreshDBToRedis(LivingRoomTypeEnum.DEFAULT_LIVING_ROOM.getCode());
                refreshDBToRedis(LivingRoomTypeEnum.PK_LIVING_ROOM.getCode());
                logger.info("开始从DB刷新到Redis");
            }
        }
    }

    private void refreshDBToRedis(Integer type) {
        // 1. 根据直播间类型查询所有的在线直播间
        List<LivingRoomRespDTO> livingRoomRespDTOS = livingRoomService.queryAllLivingRoom(type);
        String cacheKey = cacheKeyBuilder.buildLivingRoomList(type);
        String tmpListKey = cacheKey + "_tmp";
        // 2. 先搞一个临时的集合,把数据库查询到的在线直播间放到这个临时的集合中
        for (LivingRoomRespDTO livingRoomRespDTO : livingRoomRespDTOS) {
            redisTemplate.opsForList().rightPush(tmpListKey, livingRoomRespDTO);
        }
        // 3. 通过rename操作进行修改引用,我们这里并不是直接操作正在更新的直播间的key,如果直接操作正在更新的
        // 我们就先要删除掉所有的在线直播间,然后再去添加数据库查询的,这样会有网络阻塞
        // 有可能用户那变查询直播间列表的时候,直接就查不出来了
        // 还有可能用户刷新了直播间列表,查询出不完整的,破坏了完整性.
        redisTemplate.rename(tmpListKey, cacheKey);
        redisTemplate.delete(tmpListKey);
    }
}